package com.seleniti.enpareja.controller.ui.view;

import java.util.Date;

import org.ioc.commons.flowcontrol.actioncontroller.ActionController;
import org.ioc.commons.flowcontrol.eventbus.EventBus;
import org.ioc.commons.flowcontrol.logincontroller.LoginController.LoginResponse;
import org.ioc.commons.flowcontrol.operationmanager.OperationManager;
import org.ioc.commons.flowcontrol.operationmanager.impl.OperationHandlerFactory;
import org.ioc.commons.integration.dataaccess.localstorage.LocalClientStorage;
import org.ioc.commons.model.globaldata.GlobalDataController.Consumer;
import org.ioc.commons.model.globaldata.IsGlobalDataConsumer;
import org.ioc.commons.ui.features.HasInfoType.InfoType;
import org.ioc.commons.utils.FormatterLogger;

import com.seleniti.enpareja.controller.common.GlobalData;
import com.seleniti.enpareja.controller.common.LocalData;
import com.seleniti.enpareja.controller.common.LogInType;
import com.seleniti.enpareja.controller.common.domain.LoggedUserInfo;
import com.seleniti.enpareja.controller.common.domain.UserAgent;
import com.seleniti.enpareja.controller.control.controller.LoginFailedCause;
import com.seleniti.enpareja.controller.control.controller.LoginParams;
import com.seleniti.enpareja.controller.control.place.EnParejaPlace;
import com.seleniti.enpareja.controller.model.integration.service.SecurityManagementService;
import com.seleniti.enpareja.controller.ui.base.EnParejaViewBasePresenter;
import com.seleniti.enpareja.controller.ui.view.LoginView.Action;
import com.seleniti.enpareja.controller.ui.view.LoginView.Board;
import com.seleniti.enpareja.controller.ui.view.LoginView.Event;
import com.seleniti.enpareja.controller.ui.view.LoginView.Operation;

/**
 * Presenter for the Login view
 * 
 * @author Jesús Lunar Pérez
 * 
 */
public class LoginViewPresenter extends
		EnParejaViewBasePresenter<LoginView, LoginView.Event, LoginView.Operation, LoginView.Action> implements
		LoginView.Presenter, IsGlobalDataConsumer<GlobalData>, LoginResponse<LoggedUserInfo, LoginFailedCause> {

	private static final FormatterLogger logger = FormatterLogger.getLogger(LoginViewPresenter.class);

	private SecurityManagementService securityManagementService;
	private LocalClientStorage<LocalData> localClientStorage;

	public LoginViewPresenter(LoginView display, EventBus<Event> eventbus,
			OperationManager<Operation> operationManager, ActionController<Action> actionController) {
		super(display, eventbus, operationManager, actionController);
	}

	@Override
	public void onInitDisplay() {
		super.onInitDisplay();
		this.securityManagementService = this.serviceManager.serviceFactory().securityManagementService();
		this.localClientStorage = clientFactory.getLocalClientStorage();
		logger.entering("onInitDisplay");

		Consumer<GlobalData> consumer = this.globalDataController.registerGlobalDataConsumer(this);
		consumer.requires(GlobalData.UserAgent);
		consumer.ensureRequirements();

		logger.exiting("onInitDisplay");
	}

	@Override
	public void onRequiredGlobalDataReady() {

		Boolean rememberMe = (Boolean) this.localClientStorage.get(LocalData.LAST_REMEMBER_ME_CHECK_STATUS);

		display.rememberMe().setValue(rememberMe);

		if (Boolean.TRUE.equals(rememberMe)) {
			String lastUserName = (String) this.localClientStorage.get(LocalData.LAST_USER_NAME);
			display.loginName().setValue(lastUserName);
		}

		/**
		 * We only show help for getting started info when user never has logged
		 * in.
		 */
		String lastVisit = (String) this.localClientStorage.get(LocalData.LAST_LOGIN_OK);

		if (lastVisit == null || lastVisit.isEmpty()) {

			shell.gettingStartedInfoVisible().setVisible(true);
		} else {
			shell.gettingStartedInfoVisible().setVisible(false);
		}

		UserAgent ua = (UserAgent) this.globalDataController.getGlobalDataValue(GlobalData.UserAgent);

		logger.info("User agent: {0}", ua);

		if (ua == UserAgent.ANDROID_BROWSER || ua == UserAgent.ANDROID_EMBEDDED_BROWSER) {
			display.board().setValue(Board.ANDROID_LOGIN);
			display.downloadAppButtonVisibility().setVisible(ua == UserAgent.ANDROID_BROWSER);
		} else {
			display.board().setValue(Board.STANDARD_LOGIN);
		}

	}

	protected void bindComponents() {
		super.bindComponents();
		/**
		 * Starting loader when operation TryingLoading is executing
		 */
		this.display.operationManager().bindOperation(Operation.TryingLogin,
				OperationHandlerFactory.<Operation> forLoaders(display.tryingLogin()));

		/**
		 * Enabling/Disabling actions when loading operation is executing.
		 */
		this.display.operationManager().bindOperation(Operation.TryingLogin,
				OperationHandlerFactory.<Operation> forEnablers(display.actionsEnabled()));
	}

	@Override
	public void onActive() {
		super.onActive();

		shell.goBackButtonVisible().setVisible(false);
		shell.refreshButtonVisibility().setVisible(false);
		shell.firstStepsVisibility().setVisible(false);
		shell.nonAssociatedButtonVisibility().setVisible(!shell.firstStepsVisibility().isVisible());

	}

	@Override
	public void onTryLogin() {
		logger.entering("onTryLogin");

		String loginName = display.loginName().getValue();
		String loginPassword = display.loginPassword().getValue();

		if (loginName == null || loginName.isEmpty()) {
			this.messageBoxFactory.alert("Entrar en la aplicación", "El nombre de usuario no puede estar vacío");
		} else if (loginPassword == null || loginPassword.isEmpty()) {
			this.messageBoxFactory.alert("Entrar en la aplicación", "La constraseña no puede estar vacía");
		} else {
			Boolean rememberMe = display.rememberMe().getValue();
			this.loginController.tryLogin(loginName, loginPassword,
					new LoginParams(LogInType.UserPassword, display.getGoToPlaceWhenOk()), this, Operation.TryingLogin,
					this.operationManager, Boolean.TRUE.equals(rememberMe));
		}

		logger.exiting("onTryLogin");
	}

	@Override
	public void onGoToGoogleLoginView() {
		Boolean rememberMe = display.rememberMe().getValue();
		this.loginController.tryLogin(null, null, new LoginParams(LogInType.GAE, display.getGoToPlaceWhenOk()), this,
				Operation.TryingLogin, this.operationManager, Boolean.TRUE.equals(rememberMe));
	}

	@Override
	public void onLoginFailed(LoginFailedCause loginFailedCause, Throwable caught) {
		this.display.loginFailed().setVisible(true);
		this.display.infoSolveNoPasswordSet().setVisible(false);

		if (loginFailedCause == null) {
			loginFailedCause = LoginFailedCause.USER_OR_PASS_INVALID;
		}

		switch (loginFailedCause) {
		case NO_LOGIN_INFO:
			this.display.loginFailed().setType(InfoType.ERROR);
			this.display.loginFailed().setText("No se encontró información de inicio para este usuario");
			break;
		case NO_PASSWORD_SET:
			this.display.loginFailed().setType(InfoType.WARNING);
			this.display
					.loginFailed()
					.setText(
							"El usuario con el que estás intentando entrar no tiene contraseña. Esto ocurre cuando has entrado anteriormente  a la aplicación utilizando la cuenta de tu teléfono movil o una cuenta de google.");
			this.display.infoSolveNoPasswordSet().setVisible(true);
			break;
		case SERVER_ERROR_CAUSE:
			this.display.loginFailed().setType(InfoType.ERROR);
			this.display.loginFailed().setText("Se produjo un error interno en el servidor. Inténtelo más tarde.");
			break;
		case USER_OR_PASS_INVALID:
			this.display.loginFailed().setType(InfoType.ERROR);
			this.display.loginFailed().setText("Dirección de correo o contraseña no válida");
			break;
		default:
			break;
		}
		this.display.focusLoginFailed();

		/**
		 * Not logged in
		 */
		this.eventbus.fireEvent(Event.LoginFail);
	}

	@Override
	public void onLoginOk(LoggedUserInfo loggedUserInfo) {
		this.display.loginFailed().setVisible(false);
		this.display.infoSolveNoPasswordSet().setVisible(false);

		/**
		 * Storing last status at local storage (such as cookies, HTML5 local
		 * storage, etc. on a Web browser, local disk or window reg on Desktop
		 * app, and so on)
		 */
		Boolean rememberMe = display.rememberMe().getValue();

		this.localClientStorage.set(LocalData.LAST_LOGIN_OK, new Date().toString());

		this.localClientStorage.set(LocalData.LAST_REMEMBER_ME_CHECK_STATUS, rememberMe);

		if (Boolean.TRUE.equals(rememberMe)) {
			this.localClientStorage.set(LocalData.LAST_USER_NAME, display.loginName().getValue());
		}

		/**
		 * Logged in
		 */
		this.eventbus.fireEvent(Event.LoginOk);
	}

	@Override
	public void onGoToSignUpView() {
		this.placeController.goTo(EnParejaPlace.SignUp);
	}

	@Override
	public void onRequiredGlobalDataNotReadyYet() {
		/*
		 * Nothing
		 */
	}

	@Override
	public void onRequiredGlobalDataUpdated(GlobalData globalDataUpdated) {
		/*
		 * Nothing
		 */

	}

	@Override
	public void onGoToDownloadAppView() {
		this.placeController.goTo(EnParejaPlace.PreDownloadAppWarning);
	}

	@Override
	public void onGoToSolveNoPasswordSetView() {
		this.placeController.goTo(EnParejaPlace.SolveNoPasswordSet);
	}

}
